<?php

namespace fakis\core\base;

/**
 * 定义属性项
 *
 * @author Fakis <fakis738@qq.com>
 */
class DefineProp extends \yii\base\BaseObject
{
    const TYPE_ANY = 'any';
    const TYPE_STRING = 'string';
    const TYPE_INTEGER = 'integer';
    const TYPE_NUMBER = 'number';
    const TYPE_BOOLEAN = 'boolean';
    const TYPE_ARRAY = 'array';
    const TYPE_OBJECT = 'object';

    /**
     * 属性名称
     * @var string
     */
    public $name;

    /**
     * 属性类型
     * @var string
     */
    public $type = self::TYPE_ANY;

    /**
     * 属性默认值
     * @var mixed
     */
    public $default;

    /**
     * 属性标签
     * @var string|null
     */
    public $label;

    /**
     * 属性提示
     * @var string|null
     */
    public $hints;

    /**
     * 验证规则
     * @var array
     */
    public $rules = [];

    /**
     * 是否必填
     * @var bool
     */
    public $required = false;

    /**
     * 是否只读
     * @var bool
     */
    public $readOnly = false;

    /**
     * @var array
     */
    public $props = [];

    /**
     * 返回默认值
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     */
    public function getDefaultValue()
    {
        return $this->format($this->default);
    }

    /**
     * 根据数据类型格式化值
     * @param mixed $value
     * @return mixed|null
     * @throws \yii\base\InvalidConfigException
     */
    public function format($value)
    {
        if ($this->type === static::TYPE_ARRAY && !empty($this->props)) {
            return static::formatArrayModelValue($value, $this->props);
        }

        if ($this->type === static::TYPE_OBJECT && !empty($this->props)) {
            return static::formatModelValue($value, $this->props);
        }

        return static::formatValue($value, $this->type);
    }

    /**
     * 格式化数组模型属性值
     * @param mixed $value
     * @param array|DefineProp[] $props
     * @return array
     * @throws \yii\base\InvalidConfigException
     */
    public static function formatArrayModelValue($value, $props)
    {
        $result = [];
        foreach ((array)$value as $item) {
            if (is_array($item) || $item instanceof \stdClass || $item instanceof \ArrayAccess) {
                $result[] = static::formatModelValue($item, $props);
            }
        }
        return $result;
    }

    /**
     * 格式化模型属性值
     * @param mixed $value
     * @param array|DefineProp[] $props
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     */
    public static function formatModelValue($value, $props)
    {
        $model = new DefineModel(['props' => $props]);
        $model->load($value);
        return static::formatValue($model->attributes, static::TYPE_OBJECT);
    }

    /**
     * 格式化属性值
     * @param mixed $value
     * @param string $type
     * @return mixed
     */
    public static function formatValue($value, $type)
    {
        $type = strtolower($type);

        if ($type === self::TYPE_NUMBER) {
            $type = 'double';
        }

        $allows = ['boolean', 'bool', 'integer', 'int', 'float', 'double', 'string', 'array', 'object'];
        if (gettype($value) !== $type && in_array($type, $allows, true)) {
            settype($value, $type);
        }

        return $value;
    }
}